package ai.people.platform.archives.service.impl;

import ai.people.core.common.util.ApplicationContextUtil;
import ai.people.core.information.api.InformationStanderSearchService;
import ai.people.core.security.entity.AuthenticationUser;
import ai.people.core.security.support.RequestHolderUtil;
import ai.people.netmon.framework.businessenum.information.InformationSearchBehaviorEnum;
import ai.people.netmon.framework.businessenum.information.InformationSearchTypeEnum;
import ai.people.netmon.framework.domain.archives.annotation.BehaviorLog;
import ai.people.netmon.framework.domain.archives.dto.*;
import ai.people.netmon.framework.domain.archives.entity.InformationAccount;
import ai.people.netmon.framework.domain.archives.entity.InformationAccountBehavior;
import ai.people.netmon.framework.domain.archives.entity.InformationBehavior;
import ai.people.netmon.framework.domain.archives.entity.InformationScoreDetails;
import ai.people.netmon.framework.domain.archives.request.InformationSearchRequest;
import ai.people.netmon.framework.domain.archives.vo.*;
import ai.people.netmon.framework.domain.common.SimpleNameValueRespVO;
import ai.people.netmon.framework.domain.websocket.vo.WebSocketRespVO;
import ai.people.netmon.framework.model.request.PageKeyWordsRequest;
import ai.people.netmon.framework.model.response.PageResponse;
import ai.people.netmon.utils.idworker.IdWorker;
import ai.people.platform.archives.service.*;
import ai.people.platform.information.context.InformationServiceContext;
import ai.people.platform.information.properties.DataSourceProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.websocket.Session;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;

/**
 * @author LiGuanNan
 * @date 2022/6/6 16:31
 */
@Slf4j
@Service
public class InformationSearchServiceImpl implements InformationSearchService {

    @Autowired
    private InformationServiceContext informationServiceContext;

    @Autowired
    private InformationScoreDetailsService informationScoreDetailsService;

    @Autowired
    private InformationAccountBehaviorService informationAccountBehaviorService;

    @Autowired
    private InformationBehaviorService informationBehaviorService;

    @Autowired
    private InformationAccountService informationAccountService;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    /**
     * 信息检索
     *
     * @param reqParam 请求参数
     * @return {@link PageResponse}<{@link InformationSearchRespListVO}>
     */
    @Override
    @BehaviorLog(behaviorCode= InformationSearchBehaviorEnum.search)
    public PageResponse<InformationSearchRespListVO> search(InformationSearchRequest reqParam) {
        //检查配置信息
        List<DataSourceProperties> dataSourcePropertiesList = informationServiceContext.checkServerConfig();

        //dto参数转化
        InformationThirdPartySearchReqDTO reqDTO = reqDtoConverter(reqParam, reqParam.getPageSize() / dataSourcePropertiesList.size());

        List<InformationSearchRespListVO> list = new CopyOnWriteArrayList<>();
        List<InformationDataBasicFieldVO> dataList = new CopyOnWriteArrayList<>();
        List<Pair<String, Future<ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>>>> futurePairList = Lists.newArrayList();

        ExecutorService executorService = ApplicationContextUtil.getBean(ExecutorService.class, "defaultThreadPoolExecutor");
        dataSourcePropertiesList.forEach(config->{
            InformationStanderSearchService service = informationServiceContext.getThirdPartyInformationSearchService(config);
            if(service != null){
                Future<ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>> respDtoFuture
                        = executorService.submit(() -> service.search(reqDTO));
                futurePairList.add(Pair.of(service.getInformationSearchSource(), respDtoFuture));
            }
        });

        LongAdder total = new LongAdder();
        futurePairList.forEach(respFuturePair->{
            ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respResult = null;
            try {
                respResult = respFuturePair.getValue().get(20,TimeUnit.SECONDS);
                if(respResult != null && respResult.getData() != null && respResult.getData().getTotal() != null){
                    total.add(respResult.getData().getTotal());
                }
            } catch (InterruptedException | ExecutionException e) {
                log.error("Interrupted! 当前等待的线程被中止", e);
                Thread.currentThread().interrupt();
            } catch (TimeoutException e) {
                log.error("第三方查询接口请求超时");
                e.printStackTrace();
            }
            Pair<List<InformationSearchRespListVO>, List<InformationDataBasicFieldVO>> listPair = getInformationSearchRespListVos(respFuturePair.getKey(), respResult);
            list.addAll(listPair.getLeft());
            dataList.addAll(listPair.getRight());
        });

        //判断是否被查看过
        checkHasViewed(dataList, list, RequestHolderUtil.getUserId());

        //排序
        list.sort(Comparator.comparing(InformationSearchRespListVO::getHasSee).reversed());
        return PageResponse.builder(list, total.longValue(), (total.longValue() + reqParam.getPageSize() - 1) / reqParam.getPageSize(), reqParam.getOriginPageNo());
    }

    /**
     * 信息检索-同步多线程实现-列表返回详情里的所有字段
     *
     * @param reqParam 要求参数
     * @return {@link PageResponse}<{@link InformationSearchRespListVO}>
     */
    @Override
    public PageResponse<InformationSearchRespListVO> searchV2(InformationSearchRequest reqParam) {
        //检查配置信息
        List<DataSourceProperties> dataSourcePropertiesList = informationServiceContext.checkServerConfig();

        //指定查询数据源
        dataSourcePropertiesList = dataSourcePropertiesList.stream().filter(o->reqParam.getDataSourceList().contains(o.getName())).collect(Collectors.toList());

        //dto参数转化
        InformationThirdPartySearchReqDTO reqDTO = reqDtoConverter(reqParam, reqParam.getPageSize() / dataSourcePropertiesList.size());

        List<InformationSearchRespListVO> list = new CopyOnWriteArrayList<>();
        List<Pair<String, Future<ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>>>> futurePairList = Lists.newArrayList();

        ExecutorService executorService = ApplicationContextUtil.getBean(ExecutorService.class, "defaultThreadPoolExecutor");
        dataSourcePropertiesList.forEach(config->{
            InformationStanderSearchService service = informationServiceContext.getThirdPartyInformationSearchService(config);
            if(service != null){
                Future<ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>> respDtoFuture
                        = executorService.submit(() -> service.search(reqDTO));
                futurePairList.add(Pair.of(service.getInformationSearchSource(), respDtoFuture));
            }
        });

        LongAdder total = new LongAdder();
        futurePairList.forEach(respFuturePair->{
            ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respResult = null;
            try {
                respResult = respFuturePair.getValue().get(20,TimeUnit.SECONDS);
                if(respResult != null && respResult.getData() != null && respResult.getData().getTotal() != null){
                    total.add(respResult.getData().getTotal());
                }
            } catch (InterruptedException | ExecutionException e) {
                log.error("Interrupted! 当前等待的线程被中止", e);
                Thread.currentThread().interrupt();
            } catch (TimeoutException e) {
                log.error("第三方查询接口请求超时");
                e.printStackTrace();
            }
            Pair<List<InformationSearchRespListVO>, List<InformationDataBasicFieldVO>> listPair = getInformationSearchRespListVos(respFuturePair.getKey(), respResult);
            list.addAll(listPair.getLeft());
        });

        List<Future<InformationDetailRespVO>> detailFutureList = new CopyOnWriteArrayList<>();
        list.forEach(vo->{
            InformationDataBasicFieldVO basicField = vo.getBasicField();
            detailFutureList.add(executorService.submit(()-> detailWithoutScore(basicField.getDataSource(), basicField.getDataId())));
        });

        List<InformationDetailRespVO> detailRespVos = new CopyOnWriteArrayList<>();
        detailFutureList.forEach(o->{
            try {
                detailRespVos.add(o.get(10, TimeUnit.SECONDS));
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        list.forEach(o->{
            List<InformationDetailRespVO> collect = detailRespVos.stream().filter(p -> p.getBasicField().getDataId().equals(o.getBasicField().getDataId())
                    && p.getBasicField().getDataSource().equalsIgnoreCase(o.getBasicField().getDataSource())).collect(Collectors.toList());
            List<SimpleNameValueRespVO> showFields = Lists.newArrayList();
            collect.get(0).getShowFields().keySet().forEach(f->{
                showFields.add(new SimpleNameValueRespVO(f, collect.get(0).getShowFields().get(f)));
            });
            o.setShowFields(showFields);
        });

        return PageResponse.builder(list, total.longValue(), (total.longValue() + reqParam.getPageSize() - 1) / reqParam.getPageSize(), reqParam.getOriginPageNo());
    }

    /**
     * 检查已查看
     *
     * @param dataList 数据列表
     * @param list     列表
     * @param userId   用户id
     */
    private void checkHasViewed(List<InformationDataBasicFieldVO> dataList, List<InformationSearchRespListVO> list, Long userId) {
        userId = userId == null ? RequestHolderUtil.getUserId() : userId;
        List<InformationScoreDetails> viewed = informationScoreDetailsService.checkHasViewed(userId, dataList);
        if(CollectionUtils.isNotEmpty(viewed)){
            list.forEach(o->{
                if(viewed.stream().anyMatch(s->s.getDataId().equals(o.getBasicField().getDataId()) && s.getDataSource().equals(o.getBasicField().getDataSource()))){
                    o.setHasSee(1);
                }
            });
        }
    }

    /**
     * ws搜索
     *
     * @param reqParam 要求参数
     * @param msgType  msg类型
     * @param session  会话
     * @return {@link Long}
     */
    @Override
    @BehaviorLog(behaviorCode= InformationSearchBehaviorEnum.search)
    public Long wsSearch(InformationSearchRequest reqParam, String msgType, Session session) {
        //防重
        Long userId = RequestHolderUtil.getUserId();
        ValueOperations<Object, Object> opsForValue = redisTemplate.opsForValue();
        Long ttl = redisTemplate.getExpire(getRequestTimeRedisKey(userId));
        if(ttl != null && ttl > 0){
            try {
                session.getBasicRemote().sendText(JSON.toJSONString(new WebSocketRespVO<>(msgType,"请求过于频繁")));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            opsForValue.set(getRequestTimeRedisKey(userId), System.currentTimeMillis());
            redisTemplate.expire(getRequestTimeRedisKey(userId), 1, TimeUnit.SECONDS);
        }

        //缓存检查
        Long countExistingKeys = redisTemplate.countExistingKeys(Lists.newArrayList(getJxInformationSearchCache(reqParam.getSearchKeywords(), reqParam.getSearchType())));
        if(countExistingKeys != null && countExistingKeys > 0){
            try {
                session.getBasicRemote().sendText(JSON.toJSONString(new WebSocketRespVO<>(msgType, JSONArray.parseArray((String) opsForValue.get(getJxInformationSearchCache(reqParam.getSearchKeywords(), reqParam.getSearchType()))))));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return 0L;
        }

        //检查配置信息
        List<DataSourceProperties> dataSourcePropertiesList = informationServiceContext.checkServerConfig();
        if(CollectionUtils.isEmpty(dataSourcePropertiesList)){
            return 0L;
        }

        //如果是精确查询，仅查第三方数据
        if(reqParam.getSearchType() == InformationSearchTypeEnum.EXACT_MATCH.getCode()){
            dataSourcePropertiesList = dataSourcePropertiesList.stream().filter(o->o.getName().equalsIgnoreCase("FOZA")).collect(Collectors.toList());
        }

        //生产唯一搜索id
        long searchId = IdWorker.getNextId();

        //dto参数转化
        InformationThirdPartySearchReqDTO reqDto = reqDtoConverter(reqParam, reqParam.getPageSize() / dataSourcePropertiesList.size());

        //异步发起请求
        ExecutorService executorService = ApplicationContextUtil.getBean(ExecutorService.class, "defaultThreadPoolExecutor");
        dataSourcePropertiesList.forEach(config->{
            InformationStanderSearchService service = informationServiceContext.getThirdPartyInformationSearchService(config);
            if(service != null){
                executorService.execute(() -> {
                    doWsSearch(session, reqDto, config, service, msgType, searchId + "");
                });
            }
        });

        //sid绑定session
        HashOperations<Object, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(getSessionSearchIdKey(), session.getId(), searchId + "");
        redisTemplate.expire(getSessionSearchIdKey(), 10, TimeUnit.MINUTES);

        //立马返回sid
        return searchId;
    }

    /**
     * 得到jx信息搜索缓存
     *
     * @param searchKeywords 搜索关键字
     * @param searchType     搜索类型
     * @return {@link String}
     */
    private String getJxInformationSearchCache(String searchKeywords, Integer searchType) {
        return String.format("information:ws-search:%s:searchKeywords:%s", searchType, searchKeywords);
    }

    /**
     * 获取请求时间缓存
     *
     * @param userId 用户id
     * @return {@link String}
     */
    private String getRequestTimeRedisKey(Long userId) {
        return "information::ws-search::time::" + userId;
    }

    /**
     * ws搜索
     *
     * @param session  会话
     * @param reqDto   点播dto
     * @param config   配置
     * @param service  服务
     * @param msgType  msg类型
     * @param searchId 搜索id
     */
    private void doWsSearch(Session session, InformationThirdPartySearchReqDTO reqDto, DataSourceProperties config, InformationStanderSearchService service, String msgType, String searchId) {
        ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respResult = service.search(reqDto);
        Pair<List<InformationSearchRespListVO>, List<InformationDataBasicFieldVO>> pair = getInformationSearchRespListVos(config.getName(), respResult);

        List<InformationSearchRespListVO> list = pair.getLeft();
        List<InformationDataBasicFieldVO> dataList = pair.getRight();

        AuthenticationUser authenticationUser = (AuthenticationUser) Optional.ofNullable((Authentication) session.getUserPrincipal()).map(Authentication::getPrincipal).orElse(null);
        //判断是否被查看过
        if(authenticationUser != null && authenticationUser.getUserId() != null){
            checkHasViewed(dataList, list, authenticationUser.getUserId());
        }

        //排序
        list.sort(Comparator.comparing(InformationSearchRespListVO::getHasSee).reversed());

        //实时发送数据给前端
        try {
            //仅响应最近一次请求
            HashOperations<Object, String, String> hashOperations = redisTemplate.opsForHash();
            if(Objects.equals(searchId, hashOperations.get(getSessionSearchIdKey(), session.getId()))){
                session.getBasicRemote().sendText(JSON.toJSONString(new WebSocketRespVO<>(msgType, list)));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获得会话id搜索密钥
     *
     * @return {@link String}
     */
    private String getSessionSearchIdKey() {
        return "information-search::ws-session::searchId";
    }

    /**
     * 获取信息搜索resp vos列表
     *
     * @param sourceName 源名称
     * @param respResult 分别地结果
     * @return {@link Pair}<{@link List}<{@link InformationSearchRespListVO}>, {@link List}<{@link InformationDataBasicFieldVO}>>
     */
    private Pair<List<InformationSearchRespListVO>, List<InformationDataBasicFieldVO>> getInformationSearchRespListVos(String sourceName, ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respResult) {
        List<InformationSearchRespListVO> list = new CopyOnWriteArrayList<>();
        List<InformationDataBasicFieldVO> dataList = new CopyOnWriteArrayList<>();
        if(respResult != null && respResult.isSuccess()){
            for (ThirdPartyInformationSearchRespDTO dto : respResult.getData().getList()) {
                InformationSearchRespListVO vo = new InformationSearchRespListVO();
                InformationDataBasicFieldVO basicFieldVo = new InformationDataBasicFieldVO();
                basicFieldVo.setDataId(dto.getDataId());
                basicFieldVo.setDataSource(sourceName);
                vo.setBasicField(basicFieldVo);
                List<SimpleNameValueRespVO> mapList = Lists.newArrayList();
                dto.getFields().entrySet().forEach(e->{
                    SimpleNameValueRespVO nameValueRespVo = new SimpleNameValueRespVO();
                    nameValueRespVo.setName(e.getKey());
                    nameValueRespVo.setValue(e.getValue());
                    mapList.add(nameValueRespVo);
                });
                vo.setShowFields(mapList);
                vo.setHasSee(0);
                dataList.add(basicFieldVo);
                list.add(vo);
            }
        }else {
            log.error("第三方数据查询失败，数据源：【{}】，响应信息：【{}】", sourceName, respResult);
        }
        return Pair.of(list, dataList);
    }

    /**
     * 开始搜索
     *
     * @param reqParam 要求参数
     * @return {@link Long}
     */
    @Override
    @BehaviorLog(behaviorCode= InformationSearchBehaviorEnum.search)
    public Long startSearch(InformationSearchRequest reqParam) {
        //检查配置信息
        List<DataSourceProperties> dataSourcePropertiesList = informationServiceContext.checkServerConfig();
        if(CollectionUtils.isEmpty(dataSourcePropertiesList)){
            return 0L;
        }
        //生产唯一搜索id
        long sid = IdWorker.getNextId();

        //dto参数转化
        InformationThirdPartySearchReqDTO reqDTO = reqDtoConverter(reqParam, reqParam.getPageSize() / dataSourcePropertiesList.size());

        //异步发起请求
        ExecutorService executorService = ApplicationContextUtil.getBean(ExecutorService.class, "defaultThreadPoolExecutor");
        HashOperations<Object, String, Object> opsForHash = redisTemplate.opsForHash();
        dataSourcePropertiesList.forEach(config->{
            //初始化请求进度记录到redis, 进度值 0初始态，1已就绪，2已读
            opsForHash.put(informationServiceContext.getSearchProgressKey(sid), config.getName(), 0);
            opsForHash.put(informationServiceContext.getSearchResultKey(sid), config.getName(), "");
            InformationStanderSearchService service = informationServiceContext.getThirdPartyInformationSearchService(config);
            if(service != null){
                executorService.execute(() -> {
                    service.setSid(sid);
                    service.searchV2(reqDTO);
                    service.removeSid();
                });
            }
        });
        //缓存1分钟
        redisTemplate.expire(informationServiceContext.getSearchProgressKey(sid), 1, TimeUnit.MINUTES);
        redisTemplate.expire(informationServiceContext.getSearchResultKey(sid), 1, TimeUnit.MINUTES);
        //立马返回sid
        return sid;
    }


    /**
     * 得到搜索结果
     *
     * @param sid sid
     * @return {@link InformationSearchProgressVO}
     */
    @Override
    public InformationSearchProgressVO getSearchResult(Long sid) {
        HashOperations<Object, String, Object> opsForHash = redisTemplate.opsForHash();
        Map<String, Object> entries = opsForHash.entries(informationServiceContext.getSearchProgressKey(sid));
        if(entries.isEmpty()){
            return InformationSearchProgressVO.builder().list(Lists.newArrayList()).allLoaded(true).build();
        }
        List<InformationSearchRespListVO> list = new CopyOnWriteArrayList<>();
        List<InformationDataBasicFieldVO> dataList = new CopyOnWriteArrayList<>();
        entries.keySet().forEach(k->{
            if(Objects.equals(entries.get(k), 1)){
                String result = (String) opsForHash.get(informationServiceContext.getSearchResultKey(sid), k);
                ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respResult = JSONObject.parseObject(result, new TypeReference<>() {});
                Pair<List<InformationSearchRespListVO>, List<InformationDataBasicFieldVO>> pair = getInformationSearchRespListVos(k, respResult);
                list.addAll(pair.getLeft());
                dataList.addAll(pair.getRight());
                opsForHash.put(informationServiceContext.getSearchProgressKey(sid), k, 2);
            }
        });
        //判断是否被查看过
        checkHasViewed(dataList, list, null);

        //排序
        list.sort(Comparator.comparing(InformationSearchRespListVO::getHasSee).reversed());

        //判断当前是否已经全部加载完毕
        List<Object> progressList = opsForHash.multiGet(informationServiceContext.getSearchProgressKey(sid),
                informationServiceContext.getEnableServerConfigList().stream().map(DataSourceProperties::getName).collect(Collectors.toList()));
        boolean allLoaded = progressList.stream().allMatch(o -> Objects.equals(o, 2));

        return InformationSearchProgressVO.builder().list(list).allLoaded(allLoaded).build();
    }

    /**
     * 请求参数dto转换器
     *
     * @param reqParam 请求参数
     * @param pageSize 页面大小
     * @return {@link InformationThirdPartySearchReqDTO}
     */
    private InformationThirdPartySearchReqDTO reqDtoConverter(InformationSearchRequest reqParam, int pageSize) {
        InformationThirdPartySearchReqDTO reqDTO = new InformationThirdPartySearchReqDTO();
        reqDTO.setPageNo(reqParam.getOriginPageNo());
        reqDTO.setPageSize(pageSize);
        reqDTO.setSearchType(reqParam.getSearchType());
        reqDTO.setKeyword(reqParam.getSearchKeywords());
        reqDTO.setStartTime(1276155472L);
        reqDTO.setEndTime(System.currentTimeMillis()/1000);
        //reqDTO.setPlatforms(reqParam.getDataSourceList());
        return reqDTO;
    }

    /**
     * 查看详情
     *
     * @param dataSource 数据源
     * @param dataId     数据标识
     * @return {@link InformationDetailRespVO}
     */
    @Override
    @BehaviorLog(behaviorCode= InformationSearchBehaviorEnum.detail)
    public InformationDetailRespVO detail(String dataSource, String dataId) {
        InformationStanderSearchService thirdPartyInformationSearchService = informationServiceContext.getThirdPartyInformationSearchServiceBySource(dataSource);
        if(thirdPartyInformationSearchService != null){
            ThirdPartyCommonRespDTO<ThirdPartyInformationDetailRespDTO> detail = thirdPartyInformationSearchService.detail(dataId);
            if(detail != null && detail.isSuccess()){
                return InformationDetailRespVO.builder().basicField(new InformationDataBasicFieldVO(dataSource, dataId)).showFields(detail.getData().getFields()).build();
            }
        }
        return InformationDetailRespVO.builder().build();
    }

    /**
     * 不消耗积分查看详情
     *
     * @param dataSource 数据源
     * @param dataId     数据标识
     * @return {@link InformationDetailRespVO}
     */
    @Override
    public InformationDetailRespVO detailWithoutScore(String dataSource, String dataId) {
        return detail(dataSource, dataId);
    }

    /**
     * 行为历史
     *
     * @param reqParam 要求参数
     * @return {@link PageResponse}<{@link InformationBehaviorRespListVO}>
     */
    @Override
    public PageResponse<InformationBehaviorRespListVO> behaviorHistory(PageKeyWordsRequest reqParam) {
        List<InformationBehaviorRespListVO> list = Lists.newArrayList();
        LambdaQueryWrapper<InformationAccountBehavior> queryWrapper = new LambdaQueryWrapper<InformationAccountBehavior>()
                .eq(InformationAccountBehavior::getIsDelete, 0)
                .eq(InformationAccountBehavior::getUserId, RequestHolderUtil.getUserId());
        long total = informationAccountBehaviorService.count(queryWrapper);
        if(total > 0){
            Page<InformationAccountBehavior> pageParam = new Page<>(reqParam.getOriginPageNo(), reqParam.getPageSize(), total);
            queryWrapper.orderByDesc(InformationAccountBehavior::getCreateTime);
            Page<InformationAccountBehavior> page = informationAccountBehaviorService.page(pageParam, queryWrapper);
            List<InformationAccountBehavior> records = page.getRecords();
            records.forEach(o->{
                InformationBehaviorRespListVO vo = new InformationBehaviorRespListVO();
                vo.setTime(o.getCreateTime());
                vo.setKeyword(JSONObject.parseObject(o.getExtJson()).getString("keyword"));
                vo.setSpendSorce(new StringBuilder("-").append(o.getScore()).append("积分").toString());
                list.add(vo);
            });
            return PageResponse.builder(list,page.getTotal(),page.getPages(),page.getCurrent());
        }
        return PageResponse.builder(list);
    }

    /**
     * 查看列表
     *
     * @param reqParam 要求参数
     * @return {@link PageResponse}<{@link InformationSearchRespListVO}>
     */
    @Override
    public PageResponse<InformationSearchRespListVO> viewedList(PageKeyWordsRequest reqParam) {
        List<InformationSearchRespListVO> list = Lists.newArrayList();
        Long userId = RequestHolderUtil.getUserId();
        LambdaQueryWrapper<InformationAccountBehavior> queryWrapper = getViewBehaviorLambdaQueryWrapper(userId);
        long total = informationAccountBehaviorService.count(queryWrapper);
        if(total > 0){
            Page<InformationAccountBehavior> pageParam = new Page<>(reqParam.getOriginPageNo(), reqParam.getPageSize(), total);
            queryWrapper.orderByDesc(InformationAccountBehavior::getCreateTime);
            Page<InformationAccountBehavior> page = informationAccountBehaviorService.page(pageParam, queryWrapper);
            List<InformationAccountBehavior> records = page.getRecords();
            records.forEach(o->{
                InformationSearchRespListVO vo = new InformationSearchRespListVO();
                vo.setHasSee(1);
                JSONObject jsonObject = JSONObject.parseObject(o.getExtJson());
                JSONObject listShowFields = JSONObject.parseObject(jsonObject.getString("listShowFields"), Feature.OrderedField);
                List<SimpleNameValueRespVO> mapList = Lists.newArrayList();
                listShowFields.entrySet().forEach(e->{
                    SimpleNameValueRespVO nameValueRespVo = new SimpleNameValueRespVO();
                    nameValueRespVo.setName(e.getKey());
                    nameValueRespVo.setValue(e.getValue());
                    mapList.add(nameValueRespVo);
                });
                vo.setShowFields(mapList);
                vo.setBasicField(new InformationDataBasicFieldVO(jsonObject.getString("dataSource"), jsonObject.getString("dataId")));
                list.add(vo);
            });
            return PageResponse.builder(list,page.getTotal(),page.getPages(),page.getCurrent());
        }
        return PageResponse.builder(list);
    }

    /**
     * 积分概述
     *
     * @return {@link InformationViewedOverviewRespVO}
     */
    @Override
    public InformationViewedOverviewRespVO scoreOverview() {
        Long userId = RequestHolderUtil.getUserId();
        //总查看数
        LambdaQueryWrapper<InformationAccountBehavior> queryWrapper = getViewBehaviorLambdaQueryWrapper(userId);
        long total = informationAccountBehaviorService.count(queryWrapper);
        //总消费积分
        Long speedScore = informationScoreDetailsService.getSpeedScore(userId);
        //剩余可用积分
        InformationAccount account = informationAccountService.getOne(new LambdaQueryWrapper<InformationAccount>()
                .select(InformationAccount::getAvailableScore)
                .eq(InformationAccount::getUserId, userId)
                .eq(InformationAccount::getIsDelete, 0));
        return new InformationViewedOverviewRespVO(total,speedScore == null ? 0L : speedScore,account == null ? 0L : account.getAvailableScore());
    }

    /**
     * 设置jx信息搜索缓存
     *
     * @param key        关键
     * @param searchType 搜索类型
     * @param value      价值
     */
    @Override
    public void setJxInformationSearchCache(String key, Integer searchType, String value) {
        ValueOperations<Object, Object> opsForValue = redisTemplate.opsForValue();
        String jxInformationSearchCache = getJxInformationSearchCache(key, searchType);
        opsForValue.set(jxInformationSearchCache, value);
        redisTemplate.expire(jxInformationSearchCache, 10, TimeUnit.DAYS);
    }

    /**
     * 已查看查询条件包装
     *
     * @param userId 用户id
     * @return {@link LambdaQueryWrapper}<{@link InformationAccountBehavior}>
     */
    private LambdaQueryWrapper<InformationAccountBehavior> getViewBehaviorLambdaQueryWrapper(Long userId) {
        List<Long> behaviorIdList = informationBehaviorService.list(new QueryWrapper<InformationBehavior>().lambda()
                        .eq(InformationBehavior::getCode, InformationSearchBehaviorEnum.detail.getCode())
                        .eq(InformationBehavior::getIsDelete, 0))
                .stream().map(InformationBehavior::getId).collect(Collectors.toList());

        LambdaQueryWrapper<InformationAccountBehavior> queryWrapper = new LambdaQueryWrapper<InformationAccountBehavior>()
                .eq(InformationAccountBehavior::getIsDelete, 0)
                .eq(InformationAccountBehavior::getUserId, userId)
                .in(InformationAccountBehavior::getBehaviorId,behaviorIdList);
        return queryWrapper;
    }

}
